use crate::{errors::MyError, models::*};
use actix::prelude::*;
use actix_redis::{Command, RedisActor};
use actix_web::*;
use deadpool_postgres::Client;
use redis_async::resp::RespValue;
use std::time::SystemTime;
use tokio_pg_mapper::FromTokioPostgresRow;
use uuid::Uuid;

pub async fn add_user(client: &Client, name: &str) -> Result<i32, MyError> {
    let now = SystemTime::now();

    let sql = "INSERT INTO users(user_name, device_number, ip, created_at, updated_at) VALUES ($1, $2, $3, $4, $5) RETURNING user_id";
    let stmt = client.prepare(sql).await?;

    let id: i32 = client
        .query_one(&stmt, &[&name, &"", &"", &now, &now])
        .await?
        .get(0);

    Ok(id)
}

pub async fn get_user(client: &Client, name: &str) -> Result<Option<User>, MyError> {
    let sql = "SELECT * FROM users WHERE user_name = $1 LIMIT 1";
    let stmt = client.prepare(&sql).await.unwrap();

    let res = client
        .query(&stmt, &[&name])
        .await?
        .iter()
        .map(|row| User::from_row_ref(row).unwrap())
        .collect::<Vec<User>>()
        .pop();

    Ok(res)
}

pub async fn get_notice(client: &Client) -> Result<Option<Notice>, MyError> {
    let sql = "SELECT content, \"time\"::text FROM notices ORDER BY time DESC LIMIT 1";
    let stmt = client.prepare(&sql).await?;

    let res = client
        .query(&stmt, &[])
        .await?
        .iter()
        .map(|row| Notice::from_row_ref(row).unwrap())
        .collect::<Vec<Notice>>()
        .pop();

    Ok(res)
}

pub async fn get_servers(client: &Client) -> Result<Vec<Server>, MyError> {
    let sql = "SELECT * , date_part('epoch', open_time)::integer AS time_stamp FROM servers WHERE state = 1";
    let stmt = client.prepare(&sql).await?;

    let res = client
        .query(&stmt, &[])
        .await?
        .iter()
        .map(|row| Server::from_row_ref(row).unwrap())
        .collect::<Vec<Server>>();

    Ok(res)
}

pub async fn create_token(user_id: i32, redis: web::Data<Addr<RedisActor>>) -> String {
    let res = redis
        .send(Command(resp_array!["GET", format!("USER.{}", user_id)]))
        .await
        .unwrap();

    match res {
        Ok(RespValue::BulkString(token)) => {
            let _ = redis
                .send(Command(resp_array![
                    "DEL",
                    String::from_utf8(token).unwrap()
                ]))
                .await;
        }
        Ok(RespValue::SimpleString(token)) => {
            let _ = redis.send(Command(resp_array!["DEL", &token])).await;
        }
        _ => {}
    }

    let my_uuid = Uuid::new_v4();
    let new_token = my_uuid.to_string().replace("-", "");

    let _ = redis
        .send(Command(resp_array![
            "SET",
            &new_token,
            format!("{}", user_id),
            "EX",
            format!("{}", 86400 * 7)
        ]))
        .await;
    let _ = redis
        .send(Command(resp_array![
            "SET",
            format!("USER.{}", user_id),
            &new_token,
            "EX",
            format!("{}", 86400 * 7)
        ]))
        .await;

    new_token
}
